package m2.links;

import java.util.ArrayList;

import m2.component.Component;
import m2.configuration.Config;
import m2.connector.Connector;
import m2.connector.Glue;
import m2.port.Port;
import m2.role.ProvidedRole;
import m2.role.RequiredRole;
import tools.Message;

public class Binding extends Connector{
	
	private RequiredRole internalRequiredRole = new RequiredRole("internalRequiredRole");
	private ProvidedRole internalProvidedRole = new ProvidedRole("internalProvidedRole");
	private Attachment attachIn;
	private Attachment attachOut;
		
	/**
	 * 
	 */
	public Binding(){
		addGlueIn(new Glue());
		addGlueOut(new Glue());
		addRequiredRole(internalRequiredRole);
		addProvidedRole(internalProvidedRole);		
	}
	
	/**
	 * 
	 * @param ownerRequiredPort
	 * @param requiredPort
	 * @param ownerProvidedPort
	 * @param providedPort
	 */
	public final void bindingIn(Component ownerRequiredPort, Port requiredPort, Component ownerProvidedPort, Port providedPort){
		if (verifRuleDifferentAbstractLevel(ownerProvidedPort, providedPort, ownerRequiredPort, requiredPort)){
						
			attachIn = new Attachment();
			attachIn.connectPortToRole(requiredPort, internalProvidedRole);	
			
			getGlueOut().connectProvidedRoleToRequiredRole(internalProvidedRole, internalRequiredRole);	
			
			attachOut = new Attachment();
			attachOut.connectRoleToPort(internalRequiredRole, providedPort);
			
		}
	}
	
	/**
	 * 
	 * @param ownerProvidedPort
	 * @param providedPort
	 * @param ownerRequiredPort
	 * @param requiredPort
	 */
	public final void bindingOut(Component ownerProvidedPort, Port providedPort, Component ownerRequiredPort, Port requiredPort){
		if (verifRuleDifferentAbstractLevel(ownerProvidedPort, providedPort, ownerRequiredPort, requiredPort)){
			
			attachOut = new Attachment();
			attachOut.connectPortToRole( providedPort, internalRequiredRole);
			
			getGlueIn().connectRequiredRoleToProvidedRole(internalRequiredRole, internalProvidedRole);	
			
			attachIn = new Attachment();
			attachIn.connectRoleToPort(internalProvidedRole,requiredPort);
						
					
		}
	}
	
	/**
	 * 
	 * @param ownerProvidedPort
	 * @param providedPort
	 * @param ownerRequiredPort
	 * @param requiredPort
	 * @return
	 */
	private boolean verifRuleDifferentAbstractLevel(Component ownerProvidedPort, Port providedPort, Component ownerRequiredPort, Port requiredPort){
		//Verifier qu il y a un port requis et un fournis
		if (providedPort.getType().equals(requiredPort.getType())){
			Message.putInLogger("Les deux ports doivent être de types differents");
			return false;
		}
		
		//Verifier que les deux ports proviennent de composants différents
		if (ownerProvidedPort.equals(ownerRequiredPort)){
			Message.putInLogger("Les deux ports doivent être de composants différents");
			return false;
		}
		
		//Verifier que l'un des deux composants propriétaires compose l'autre		
		ArrayList<Component> listeCompProvided = ownerProvidedPort.getListeComponent();
		ArrayList<Component> listeCompRequired = ownerRequiredPort.getListeComponent();
		if (!verifComposition(ownerProvidedPort.getConfig(), listeCompProvided, ownerRequiredPort) &&
				!verifComposition(ownerRequiredPort.getConfig(), listeCompRequired, ownerProvidedPort)){
			Message.putInLogger("L'un des deux composants propriétaire doit composer l'autre");
			return false;
		}
		
		return true;
	}
	
	/**
	 * 
	 * @param config
	 * @param listeComp
	 * @param comp
	 * @return
	 */
	private boolean verifComposition(Config config, ArrayList<Component> listeComp, Component comp){
		boolean find = false;
		for (Component compo : listeComp){
			if (compo == comp){
				find = true;
			}
		}
		
		if (!find){
			if (config == comp){
				find = true;
			}
		}
		
		return find;
	}
	
	/**
	 * 
	 * @return
	 */
	public Attachment getAttachProvied() {
		return attachIn;
	}

	/**
	 * 
	 * @return
	 */
	public Attachment getAttachRequired() {
		return attachOut;
	}
	
	public RequiredRole getInternalRequiredRole() {
		return internalRequiredRole;
	}

	public ProvidedRole getInternalProvidedRole() {
		return internalProvidedRole;
	}

	public Attachment getAttachIn() {
		return attachIn;
	}

	public Attachment getAttachOut() {
		return attachOut;
	}
	
	public void setAttachIn(Attachment attachIn) {
		this.attachIn = attachIn;
	}

	public void setAttachOut(Attachment attachOut) {
		this.attachOut = attachOut;
	}
}
